perm filename TCPOPT.MAC[IP,SYS] blob sn#680224 filedate 1982-10-14 generic text, type T, neo UTF8
;CWL:<403-TCP>TCPOPT.MAC.40303  2-May-82 16:56:04, Edit by CLYNN
; Fix TLH not 32 bit bug
; Added new option routines.  Old ones in limbo
;<403-TCP>TCPOPT.MAC.40301 29-Jan-82 15:05:57, Edit by CLYNN
; Updated for TCP release 3

	SEARCH	INPAR,TCPPAR,PROLOG
	TTITLE	TCPOPT
	SUBTTL	TCP Option Routines, William W. Plummer, 5FEB77
	SWAPCD
COMMENT	!
	These routines perform various functions associated with
	TCP and Internet Options.  Options are stored in packets
	after the header and before the data.  The format is
	described in TCPPAR.

* TCPIIO ...  3 ...... Insert (or refresh) IP options
* TCPITO ...  5 ...... Insert TCP options

  OPSAV ....  6 ...... Save registers for option routines
              6 ...... Internal register definitions
              7 ...... Option tables
* TCPUOP ...  9 ...... Process & merge User options
  PARSOP ... 10 ...... Parse and validate user specified options
  LSRCHK ... 11 ...... Validate Loose Source Route option
  SSRCHK ... 11 ...... Validate Strict Source Route option
* TCPXIO ... 12 ...... Extract, process & merge IP options
* TCPXTO ... 12 ...... Extract, process & merge TCP options
  TCPXXO ... 13 ...... Work routine for TCPXIO & TCPXTO
  MSLXCT ... 15 ...... Process TCP Max Seg Len option
  MERGE .... 16 ...... Merge User & Received options
  MERGED ... 17 ...... Drop option from merge
  RRTCPY ... 18 ...... Invert received RRT option to SSR
  LSRCPY ... 19 ...... Invert received LSR option
  SSRCPY ... 19 ...... Invert received SSR option
  TSPCPY ... 21 ...... Copy received TSP option
  SIDCPY ... 21 ...... Copy received SID option
             22 ...... IP/TCP Information tables
  OPRES .... 22 ...... Restore saved registers

* SNDSOP ... 23 ...... Send Secure Open (Internet)
* SNDSCL ... 23 ...... Send Secure Close (Internet)
* SETLAB ... 24 ...... Set debugging label (TCP)
* GETLAB ... 24 ...... Get debugging label (TCP)
* RSVTSO ... 25 ...... Reserve space for a local timestamp option (TCP)
* SETTSO ... 25 ...... Set the value field of local timestamp (TCP)
* GETTSO ... 25 ...... Get the value field from local timestamp (TCP)
* SETRBS ... 26 ...... Set receive buffer size option (TCP)
* GETRBS ... 26 ...... Get receive buffer size (TCP)
  GET4 ..... 27 ...... Get a 4-byte value
  SET4 ..... 27 ...... Set a 4-byte value

  INOPTR ... 28 ...... Get byte pointer for adding an Internet option
  TCOPTR ... 29 ...... Get byte pointer for adding a TCP option
  FNINOP ... 30 ...... Get byte pointer for reading an Internet opton
  FNTCOP ... 30 ...... Get byte pointer for reading a TCP opton
  SCANOP ... 31 ...... Lookup an option
	!

; TCPIIO	Insert IP Options (from TCB image) into packet

; PKT/	(ext) pointer to packet being constructed
; TCB/	(ext) pointer to TCB  or  0
;	CALL TCPIIO
;Ret+1:	Always, Any options inserted & PIPL & PIDO updated accordingly

TCPIIO::JUMPE TCB,TCPIIX	; No TCB

	LOAD T1,TIPDO,(TCB)	; TOTAL IP header size, words
	SUBI T1,<MINIHS+3>/4	; Minus standard header
	JUMPLE T1,TCPIIX	; No IP options

	LOAD T2,PIDO,(PKT)	; IP header size, words
	CAIE T2,<MINIHS+3>/4	; Insert or refresh?
	  JRST TCPII5		; Refresh

; Insert options

	ADD T1,T2		; Desired header+options
	CAILE T1,.RTJST(-1,PIDO) ; Too big?
	  MOVX T1,.RTJST(-1,PIDO) ; Clamp to max
	STOR T1,PIDO,(PKT)	; Update IP header size, words
	SUB T1,T2		; Room for options, words

	MOVE T4,T1		; Option words
	LSH T4,2		; Bytes in option words
	LOAD T2,PIPL,(PKT)	; Current packet length
	ADD T2,T4		; New packet length, bytes
	STOR T2,PIPL,(PKT)	; Updated packet length
	JRST TCPII8

; Refresh options

TCPII5:	MOVE T1,T2		; Available space
	SUBI T1,<MINIHS+3>/4	; for options

TCPII8:

; Copy options into header

	XMOVEI T2,TCBIO(TCB)	; Option image in TCB
	XMOVEI T3,PKTELI(PKT)	; IP header base
	ADDI T3,<MINIHS+3>/4	; Pointer to IP option area
	CALL XBLTA		; Options into header
TCPIIX:
	RET

REPEAT 0,<

	SKIPN TCB		; Have a TCB?
	  RET			; No, no options

; If the TCP is running in secure mode, include a Secure Open
; (i.e., connection change request) if the security level has changed.
; Do this for a SYN packet too even though the level will most likely
; be 0.  This means "security level not yet known" to the KDC.

	LOAD T1,TSSYN,(TCB)	; Get send state
	LOAD T2,TSLVN,(TCB)	; "Next" Security Level
	LOAD T3,TSLVC,(TCB)	; "Current" level
	CAIE T1,SYNABL		; About to send a SYN?
	 CAME T2,T3		; Or difference in levels?
	  SKIPN INTSCR		; And running in secure mode?
	   CAIA			; No.
	    CALL SNDSOP		; Yes.  Send the option

	RET

> ; End of REPEAT 0

; TCPITO	Insert TCP options (from TCB image) into packet

; PKT/	(ext) pointer to packet being constructed
; TPKT/	(ext) pointer to TCP portion of packet
; TCB/	(ext) pointer to TCB  or  0
;	CALL TCPITO
;Ret+1:	Always, any options copied & PIPL and PTDO updated

TCPITO::JUMPE TCB,TCPITX	; No TCB

	LOAD T1,TTPDO,(TCB)	; TOTAL TCP header size, words
	SUBI T1,<MINTHS+3>/4	; Minus standard header
	JUMPLE T1,TCPITX	; No TCP options

	LOAD T2,PTDO,(TPKT)	; TCP header size, words
	MOVE T3,TPKT		; TCP header base
	ADD T3,T2		; Pointer to TCP option area

	ADD T1,T2		; Desired header+options
	CAILE T1,.RTJST(-1,PTDO) ; Too big?
	  MOVX T1,.RTJST(-1,PTDO) ; Clamp to max
	STOR T1,PTDO,(TPKT)	; Update TCP header size, words
	SUB T1,T2		; Room for options, words

	MOVE T4,T1		; Option words
	LSH T4,2		; Bytes in option words
	LOAD T2,PIPL,(PKT)	; Current packet length
	ADD T2,T4		; New packet length, bytes
	STOR T2,PIPL,(PKT)	; Updated packet length

	XMOVEI T2,TCBTO(TCB)	; Option image in TCB
	CALL XBLTA		; Options into header
TCPITX:
	RET


REPEAT 0,<
	AOSA T1,NXTLBL		; Get next packet label
	  JFCL
	SKIPE INTTRC		; Tracing packets?
	  CALL SETLAB		; Store in packet (LBLOPT)

	SKIPE STATF		; Taking statistics right now?
	  CALL RSVTSO		; Yes, reserve timestamp option space

	SKIPN TCB		; Have a TCB?
	  RET			; No, no more options

	RET
> ; End of REPEAT 0
; Maximum # words for options

MAXIOW==1←<WID(PIDO)>-1-<MINIHS+3>/4	; Max # IP option words
MAXTOW==1←<WID(PTDO)>-1-<MINTHS+3>/4	; Max # TCP option words
MAXOPW==MAXIOW				; Max of the two
IFG <MAXTOW-MAXIOW>,<MAXOPW==MAXTOW>


; AC redefinitions - Temp and Local register definitions initiated
; by CALL OPSAV


OPSAV:	STACKL <UPTRS,<UOPS,MAXOPW+1>,<REGS,11+1>>
	MOVEM 11,11+REGS	; Save last reg
	MOVEI 11,REGS		; 0,,save area adr
	BLT 11,11-1+REGS	; Save others

	MOVX CX,<MSEC1,,R>	; Wipe out our
	EXCH CX,-2+UPTRS	; return with dummy
	CALL (CX)		; Back to mainline
	JRST OPRES		; Go clean up stack


OPTS==0 ;DEFAC(OPTS,FR)		; Bit corresponding to options present

RP==2 ;DEFAC(RP,T2)		; Pointer to received option byte
RC==3 ;DEFAC(RC,T3)		; Count of remaining received option bytes
BIT==4 ;DEFAC(BIT,T4)		; Bit corresponding to current option

OPT==5 ;DEFAC(OPT,Q1)		; Option (byte)
OPL==6 ;DEFAC(OPL,Q2)		; Option length
OPC==7 ;DEFAC(OPC,Q3)		; Count of remaining option bytes

OPP==10 ;DEFAC(OPP,P1)		; Pointer to option byte
IORT==11 ;DEFAC(IORT,11)	; Pointer to IP or TCP Info table
;	TPKT
;	PKT
;	TCB

; Option Tables

DEFINE OPTION (T,NA,C,NU,L,U,R,E)<
	EXP NU
> ; End of DEFINE OPTION

	-NIPOP,,.+1	; IP option numbers
IOPNU:	IPOPTS
NIPOP==.-IOPNU		; # IP options
	ENDOPT

	-NTCPOP,,.+1	; TCP option numbers
TOPNU:	TCPOPTS
NTCPOP==.-TOPNU		; # TCP options
	ENDOPT

DEFINE OPTION (T,NA,C,NU,L,U,X)<
IFDEF  NA'U,<	MSEC1,,NA'U	>
IFNDEF NA'U,<	MSEC1,,X	>
> ; End of DEFINE OPTION

OPTCOF==.-IOPNU		; Is user option valid
	IPOPTS  (CHK,RSKP)	; Known IP Options
	MSEC1,,RSKP	; Unknown IP Options - OK if cannot prove wrong
	MSEC1,,RSKP	; Maintain spacing
	TCPOPTS (CHK,RSKP)	; Known TCP Options
	MSEC1,,RSKP	; Unknown TCP Options - OK if cannot prove wrong

OPTDOF==.-IOPNU		; Should received option be dropped or sent?
	IPOPTS  (CPY,R)	; Known IP Options
	MSEC1,,R	; Unknown IP Options - Drop unknowns
	MSEC1,,R	; Maintain spacing
	TCPOPTS (CPY,R)	; Known TCP Options
	MSEC1,,R	; Unknown TCP Options - Drop unknowns

OPTXOF==.-IOPNU		; What does received option mean?
	IPOPTS  (XCT,R)	; Known IP Options
	MSEC1,,R	; Unknown IP Options - Forget unknowns
	MSEC1,,R	; Maintain spacing
	TCPOPTS (XCT,R)	; Known TCP Options
	MSEC1,,R	; Unknown TCP Options - Forget unknowns

DEFINE OPTION (T,NA,C,NU,L,U,R,E)<
NA'LEN==L
> ; End of DEFINE OPTION

	IPOPTS
	TCPOPTS

; Process User specified options

; T1/	(ip,,tcp) user section 0 option addresses (or -1 to re-merge)
; TCB/	(ext) pointer to locked connection block
;	CALL TCPUOP
;Ret+1:	Always, T1 is 0 or error code


TCPUOP::CALL OPSAV		; Save regs & get working area
	MOVEM T1,UPTRS		; Save user addresses

	HLRZ T2,UPTRS		; User address of IP options
	JUMPE T2,TCPUO2		; None
	XMOVEI IORT,IPINFO	; IP Info
	SETZ T1,		; Incase re-merge
	CAIE T2,-1		; Skip PARSOP if re-merge
	  CALL PARSOP		; Parse and validate user specified options
	JUMPN T1,TCPUOX		; Error
	CALL MERGE		; Merge new user options with received
TCPUO2:
	HRRZ T2,UPTRS		; User address of TCP options
	JUMPE T2,TCPUO4		; None
	XMOVEI IORT,TCPINFO	; TCP Info
	SETZ T1,		; Incase re-merge
	CAIE T2,-1		; Skip PARSOP if re-merge
	CALL PARSOP		; Parse and validate user specified options
	JUMPN T1,TCPUOX		; Error
	CALL MERGE		; Merge new user options with received
TCPUO4:
	SETZ T1,		; All ok
TCPUOX:	MOVEM T1,T1+REGS	; Return value
	RET
; PARSOP  Parse and validate user specified options

; T2/	(section 0) user address of options (non-zero if here)
; IORT/	(Ext) pointer to IP or TCP Info table
;	CALL PARSOP
;Ret+1:	Always, T1 non-zero if error

PARSOP:	SETZM OPTS		; No user options yet
	MOVE T1,MXW(IORT) ;MAXxOW ; Max possible length, w
	XMOVEI T3,UOPS		; Our local copy
	CALL BLTUM		; Get user options

	MOVE T3,MXW(IORT) ;MAXxOW ; Maximum length, w
	LSH T3,2		; Bytes
	MOVE OPC,T3		; Used later too
	MOVX OPP,<POINT 8,UOPS,7> ; Byte pointer to first
PARSO2:	LDB OPT,OPP		; Get option
	ANDCM OPT,CPY(IORT) ;CPYOPT/0 ; Without copy on fragmentation
	CAIN OPT,ENDOPT		; End?
	  JRST PARSO6		; Yes
	CAIN OPT,NOPOPT		; NOP?
	  JRST PARSO4		; Yes

	MOVE T1,OTAB(IORT) ;MSEC1,,xOPNU ; Address of option table
	MOVE T1,-1(T1)		; -count,,address of first
	CAME OPT,(T1)		; This it?
	  AOBJN T1,.-1		; No, try next
	HLRE BIT,T1		; -i or 0
	SKIPE BIT		; Unknown option
	  MOVE BIT,BITS+↑D36(BIT) ; Known option

	CALL @OPTCOF(T1)	; Check option
	  JRST PARSO9		; Looses

	ILDB OPT,OPP		; Get length (maybe)
	CAIL OPC,2		; Enough for length?
	 CAMGE OPC,OPT		; Enough for option?
	  JRST PARSO9		; No, error
	IORM BIT,OPTS		; Remember option exists
	SUBI OPC,1		; Count option byte
	SUBI OPT,1		; Already past it
IFN NOPOPT-1,<
	CAIA
PARSO4:	  MOVX OPT,1		; Option length is 1 (NOPOPT)
> ; End of IFN NOPOPT-1
IFE NOPOPT-1,<PARSO4:>		; OPT code is length
	SUB OPC,OPT		; Free bytes after this option
	ADJBP OPT,OPP		; Point at next
	MOVEM OPT,OPP
	JUMPG OPC,PARSO2	; Back for next option
PARSO6:

	SUB T3,OPC		; Used bytes
	JUMPLE OPC,PARSO7	; No free bytes
	SETZ T1,

	IDPB T1,OPP		; Clear free bytes
	SOSLE OPC
	  JRST .-2
PARSO7:
	XCT SOU(IORT) ;STOR T3,TxPOU,(TCB) ; Store user option bytes
	MOVE T1,MXW(IORT) ;MAXxOW ; Max possible length, w
	XMOVEI T2,UOPS		; New options
	XCT XTOU(IORT) ;XMOVEI T3,TCBxU(TCB) ; TCB location
	CALL XBLTA		; Copy them there
	XCT SOF(IORT) ;STOR OPTS,TxOPF,(TCB) ; Save flags

	TDZA T1,T1		; No error
PARSO9:	  HRROI T1,ELP+↑D2	; Error
	RET

; Make sure routing options end with packet source address
; T1/		Option table index (free)
; T2/		(free)
; T3/		Max count (preserve)
; T4=BIT/	Bit representing option (preserve)
; OPT/		Option code w/o CPYOPT (free)
; OPP/		Pointing at option code byte (preserve)
; OPC/		Remaining count (preserve)
; TCB/		Locked (ext) TCB address
;	CALL LSRCHK or SSRCHK
; Return+1	Error in option
; Return+2	Ok

LSRCHK:	SSRCHK:
	PUSH P,OPP		; Save pointer
	JE TLH,(TCB),RUTCKO	; Not yet specified so cannot check
	ILDB T2,OPP		; Length
	ILDB OPT,OPP		; Initial pointer
	CAIL T2,7		; 7 is leagal but 13(8) is reasonable
	 CAIE OPT,4
	  JRST RUTCKE		; Bad
	SUBI T2,3		; Option header length
RUTCKL:	ILDB OPT,OPP		; Next address byte
	LSH T1,↑D8		; Room for another byte
	IOR T1,OPT		; of address
	SOSLE T2		; More bytes?
	  JRST RUTCKL		; Yes
	ANDX T1,<BYTE (4)0(8)377,377,377,377> ; Just last 4 bytes
	LOAD T2,TLH,(TCB)	; Source
	CAMN T1,T2		; Same?
RUTCKO:	  AOS -1(P)		; OK, skip return
RUTCKE:	POP P,OPP		; Restore register
	RET

; Extract interesting IP options from received packet

; PKT/	(ext) pointer to IP header
; TCB/	(ext) pointer to locked TCB
;	CALL TCPXIO
;Ret+1:	Always, options updated/processed

TCPXIO::LOAD T1,PIDO,(PKT)	; IP header size, w
	SUBI T1,<MINIHS+3>/4	; Size w/o options
	JUMPLE T1,TCPXIX	; None

	CALL OPSAV		; Save regs & get working area

	MOVE T2,PKT
	ADDI T2,PKTELI+<MINIHS+3>/4
	XMOVEI IORT,IPINFO	; IP Info
	CALL TCPXXO		; Process received IP options
TCPXIX:
	RET




; Extract interesting TCP options from received packet

; TPKT/	(ext) pointer to TCP header
; TCB/	(ext) pointer to locked TCB
;	CALL TCPXTO
;Ret+1:	Always, options updated/processed

TCPXTO::LOAD T1,PTDO,(TPKT)	; TCP header size, w
	SUBI T1,<MINTHS+3>/4	; Size w/o options
	JUMPLE T1,TCPXTX	; None

	CALL OPSAV		; Save regs & get working area

	MOVE T2,TPKT
	ADDI T2,<MINTHS+3>/4
	XMOVEI IORT,TCPINFO	; TCP Info
	CALL TCPXXO		; Process TCP options
TCPXTX:
	RET
; Scan options

; T1/	# option words
; T2/	(ext) pointer to options in packet
; IORT/	(Ext) Information table address
;	CALL TCPXXO
;Ret+1:	Always

TCPXXO:	PUSH P,T1		; Save received option words

	SETZM UOPS		; Clear working area
	MOVEI T4,UOPS		; Build BLT word
	HRLS T4
	ADDI T4,1		; But save T2
	BLT T4,MAXOPW-1+UOPS

	MOVE T1,(P)		; Words received
	XMOVEI T3,UOPS
	CALL XBLTA		; Get options from packet

	MOVE RC,(P)		; Words received
	LSH RC,2		; Bytes
	MOVEM RC,(P)		; Save for later
	MOVX RP,<POINT 8,UOPS,7> ; First received option

TCPXX3:	LDB OPT,RP		; Reveived option byte
	ANDCM OPT,CPY(IORT) ;CPYOPT/0 ; Without copy on fragmentation
	CAIN OPT,ENDOPT		; End of options?
	  JRST TCPXX7		; Yes, all done
	CAIN OPT,NOPOPT		; Watch out for length 1
	  JRST TCPXX5

	MOVE T1,OTAB(IORT) ;MSEC1,,xOPNU ; Option table address
	MOVE T1,-1(T1)		; AOBJN pointer for options
	CAME OPT,(T1)		; This the option?
	  AOBJN T1,.-1		; No, look further

	ILDB OPT,RP		; Get option length
	CALL @OPTXOF(T1)	; Interpret option

	LDB OPT,RP		; Option length
	SUBI RC,1		; Count option code
	SUBI OPT,1		; Already past it
IFN NOPOPT-1,<
	CAIA
TCPXX5:	  MOVX OPT,1		; Option length is 1 (NOPOPT)
> ; End of IFN NOPOPT-1
IFE NOPOPT-1,<TCPXX5:>		; OPT code is length
	SUB RC,OPT		; Bytes left to process
	ADJBP OPT,RP		; Option byte
	MOVEM OPT,RP

	JUMPG RC,TCPXX3		; Loop if more
TCPXX7:

	MOVNS RC
	ADD RC,(P)		; Acutal option bytes
	XCT SOR(IORT) ;STOR RC,TxPOR,(TCB)
	SUB P,BHC+1		; Restore stack

	MOVE T1,MXW(IORT) ;MAXxOW
	XMOVEI T2,UOPS
	XCT XTOR(IORT) ;XMOVEI T3,TCBxR(TCB)
	CALL XBLTA		; Save options for future

	CALL MERGE		; Merge them with user's
	RET

; Received TCP Maximum Segment Length Option

; RP/	Points to length byte
;	CALL MSLXCT
;Ret+1:	Always, TSMXP updated if possible


MSLXCT:	PUSH P,RP		; Save temps
	PUSH P,RC
	MOVE T3,RP
	LDB T4,T3		; Get length
	CAIE T4,MSLLEN		; Right length?
	  JRST MSLXCX		; No, ignore
				; (ICMP error msg??)
	SETZ T1,
	MOVX T4,<-2,,0>		; Two more bytes
MSLXC4:	ILDB T2,T3		; Accumulate #
	LSH T2,↑D<36-8>
	LSHC T1,↑D8
	AOBJN T4,MSLXC4

	CALL TCPMXP		; Compute new segment length

MSLXCX:	POP P,RC
	POP P,RP
	RET
; MERGE User options and received options into send options

; IORT/	IP/TCP Information table address
;	CALL MERGE

MERGE:	XCT XFOU(IORT) ;XMOVEI T2,TCBxU(TCB) ; Assume User options
	XCT LOU(IORT) ;LOAD OPP,TxPOU,(TCB) ; User bytes used
	XCT LOR(IORT) ;LOAD RC,TxPOR,(TCB) ; Received bytes used
	JUMPE RC,MERGE8		; Nothing to merge - User only

	MOVE OPC,MXW(IORT) ;MAXxOW ; Max words allowed
	LSH OPC,2		; Max bytes allowed
	SUB OPC,OPP		; Free bytes after User
	JUMPLE OPC,MERGE8	; No room for merge - User only

; Either merge or copy/drop received options

	ADJBP OPP,[POINT 8,UOPS,7] ; Point at free output byte

	MOVE T1,MXW(IORT) ;MAXxOW ; Begin with User options
	XMOVEI T3,UOPS		; Get user options
	CALL XBLTA

	MOVE RP,POR(IORT) ;POINT 8,TCBxR(TCB),7>) ; Received options
	XCT LOR(IORT) ;LOAD RC,TxPOR,(TCB) ; Get received bytes
	XCT LOF(IORT) ;LOAD OPTS,TxOPF,(TCB) ; Get User option flags

MERGE3:	LDB OPT,RP		; Reveived option byte
	ANDCM OPT,CPY(IORT) ;CPYOPT/0 ; Without copy on fragmentation
	CAIN OPT,ENDOPT		; End of options?
	  JRST MERGE5		; Yes, All done
	CAIN OPT,NOPOPT		; Nop?
	  JRST [SUBI RC,1	; Length
		ILDB OPT,RP	; Point at next
		JRST MERGE4]	; Go to loop

; Option with length

	MOVE T1,OTAB(IORT) ;MSEC1,,xOPNU ; Option table address
	MOVE T1,-1(T1)		; AOBJN pointer for options
	CAME OPT,(T1)		; This the option?
	  AOBJN T1,.-1		; No, look further
	HLRE BIT,T1		; -i (or 0)
	SKIPE BIT
	  MOVE BIT,BITS+↑D36(BIT) ; 1B<36-i>

	LDB OPT,RP		; Copy option
	DPB OPT,OPP
	ILDB OPT,RP		; Get length
	SUB RC,OPT		; Received bytes after this option
	MOVEI OPL,-1(OPT)	; # bytes left to copy in this option

	TRNN OPTS,(BIT)		; If User specified, drop
	 CAMLE OPT,OPC		; or if not enough space
	  CAIA			; Go drop it
	   CALL @OPTDOF(T1)	; Try to copy it
	    CALL MERGED		; Drop it
MERGE4:
	SKIPLE RC		; If more left
	  JUMPG OPC,MERGE3	; and room, loop

MERGE5:
	MOVX OPT,ENDOPT		; End options (may overflow
	DPB OPT,OPP		; into guard word ending UOPS)

	XMOVEI T2,UOPS		; Copy from merged

	MOVE OPP,MXW(IORT) ;MAXxOW ; Maximum option words
	LSH OPP,2		; Bytes
	SUB OPP,OPC		; Used bytes

MERGE8:	;(T2,OPP) Enter here if no merge, just copy user to send

	ADDI OPP,3		; Round bytes up to
	LSH OPP,-2		; Option words used
	SKIPE OPP		; If none, 0 is ok
	  ADDI OPP,<MINIHS+3>/4	; Including header
	XCT SDO(IORT) ;STOR OPP,TxPDO,(TCB) ; Set send header length

	MOVE T1,MXW(IORT) ;MAXxOW ; Maximum option words
	XCT XTOO(IORT) ;XMOVEI T3,TCBxO(TCB) ; To TCB image
	CALL XBLTA		; Go the options

	RET



; MERGED Drop option from net

; RP/	Points at Len byte
; OPT/	Len of option
; OPL/	Len-1 of option
; OPC/	Len of option
; OPP/	Points at output option byte (already copied)
;	CALL MERGED
; RP/	Points at next option
; OPC/	Unchanged since none used
; OPP/	Unchanged since none used


MERGED:	ADJBP OPL,RP
	MOVEM OPL,RP		; Point at next
	RET

; Change Record Route from net into Strict Source Route for reply

; RP/	Points at Len byte
; OPT/	Len of option
; OPL/	Len-1 of option
; OPC/	Len of option
; OPP/	Points at output option byte (already copied)
;	CALL RRTCPY
;Ret+1:	  Option should be dropped
;Ret+2:	Option was copied and
; RP/	Points at next option
; OPC/	Count reduced if copied
; OPP/	Pointer updated if copied


RRTCPY:	PUSH P,[0]		; Turn it into a (trimmed) SSR

	MOVE T1,RP		; Peek ahead at
	ILDB T4,T1		; Pointer
	CAIN OPT,-1(T4)		; At end? (Len vs (Ptr)-1)
	  JRST RRTDS2		; Yes

	MOVE T1,OPT		; Len reserved for RRT
	MOVEI OPT,-1(T4)	; Len actually used
	SUB T1,OPT		; Unused bytes in RRT option
	MOVEM T1,(P)		; Save for RP adjustment
RRTDS2:

	MOVX T1,SSROPT		; Change RRT
	DPB T1,OPP		; to SRT
	CALL NUSSR		; Process as Strict Source Route
	  JRST RRTDSX		; Have to drop it (OPL still Len-1)

	POP P,T1		; Unused bytes in RRT are
	ADJBP T1,RP		; Skipped
	MOVEM T1,RP		; over

	AOS (P)			; All OK return
	RET



RRTDSX:	SUB P,BHC+1		; Clear stack of unused RRT bytes
	RET			; Dropped return
; Invert Record Route or Loose or Strict Source Route from net for reply

; RP/	Points at Len byte
; OPT/	Len of option
; OPL/	Len-1 of option
; OPC/	Len of option
; OPP/	Points at output option byte (already copied)
;	CALL LSRCPY  or  SSRCPY  or  NUSSR
;Ret+1:	  Option should be dropped
;Ret+2:	Option was copied and
; RP/	Points at next option
; OPC/	Count reduced if copied
; OPP/	Pointer updated if copied


LSRCPY:
SSRCPY:				; Invert address list

	MOVE T1,RP		; Peek ahead at
	ILDB T4,T1		; LSR/SSR pointer
	TRNN T4,3		; Pointer multiple of 4 and
	 CAIE OPT,-1(T4)	; At end? (Len vs (Ptr)-1)
	  RET			; No, error, drop it (shouldn't get here)
NUSSR:	PUSH P,RC		; Save register for our use
	PUSH P,OPP		; Pointer to (copied) option code
	IDPB OPT,OPP		; Copy option length
	SUB OPC,OPT		; Remaining free bytes tobe

	ILDB RC,RP		; Old pointer changed to
	LSH RC,-2		; Counter of addresses
	SUBI RC,1		; (omit code,len,ptr)
	MOVX OPT,4		; Initial pointer
	IDPB OPT,OPP

	PUSH P,[-1]		; Marker
INVRI:	MOVX T4,<-4,,0>		; Collect 4-byte addresses
	PUSH P,BHC ;[0]		; here
	MOVX T1,<POINT 8,(P),3>	; Point to it (right justified)
	ILDB OPT,RP
	IDPB OPT,T1		; Pack
	AOBJN T4,.-2
	SOSLE RC
	  JRST INVRI		; Another address to pack

INVRO:	MOVX T4,<-4,,0>		; Pack 4-byte addresses
	MOVX T1,<POINT 8,(P),3>	; From top of stack (right justified)
	ILDB OPT,T1
	IDPB OPT,OPP		; Into send option
	AOBJN T4,.-2

	POP P,T1		; Save processed address
	SKIPL (P)		; Reached marker?
	  JRST INVRO		; No, do another
	SUB P,BHC+1		; Drop marker

	LOAD T4,TFH,(TCB)	; Destination must be last address in route
	CAIN T1,T4		; Same as last?
	  JRST NDSSR		; Yes all ok

	MOVE T1,(P)		; Pointer to option code byte
	MOVX OPT,LSROPT		; If adding, make into loose route
	DPB OPT,T1

	ILDB OPT,T1		; Must increase length by another address
	ADDI OPT,4
	DPB OPT,T1

	SUBI OPC,4		; Using 4 more bytes
	JUMPL OPC,NDSSR		; Overran max header length, lose

	MOVEM T4,(P)		; Have to append destination address

	MOVX T4,<-4,,0>		; Pack 4-byte addresses
	MOVX T1,<POINT 8,(P),3>	; From top of stack (right justified)
	ILDB OPT,T1
	IDPB OPT,OPP		; Into send option
	AOBJN T4,.-2

NDSSR:	SUB P,BHC+1		; Drop pointer to option byte

	ILDB OPT,RP		; Point at next received and
	ILDB OPT,OPP		; Send option position
	POP P,RC		; Restore register
	AOS (P)			; Skip return since copied
	RET

; Copy Received option if reserved space not all used

; RP/	Points at Len byte
; OPT/	Len of option
; OPL/	Len-1 of option
; OPC/	Len of option
; OPP/	Points at output option byte (already copied)
;	CALL TSPCPY
;Ret+1:	  Option should be dropped
;Ret+2:	Option was copied and
; RP/	Points at next option
; OPC/	Count reduced if copied
; OPP/	Pointer updated if copied


TSPCPY:
	MOVE T1,RP		; Peek ahead at
	ILDB T4,T1		; Pointer
	CAIN OPT,-1(T4)		; At end? (Len vs (Ptr)-1)
	  RET			; Yes, drop it
				; No, fall into copy
; Fall into copy



; Copy Received option

; RP/	Points at Len byte
; OPT/	Len of option
; OPL/	Len-1 of option
; OPC/	Len of option
; OPP/	Points at output option byte (already copied)
;	CALL SIDCPY
;Ret+1:	  Option should be dropped
;Ret+2:	Option was copied and
; RP/	Points at next option
; OPC/	Count reduced if copied
; OPP/	Pointer updated if copied


SIDCPY:				; Copy option

	SUB OPC,OPT		; Remaining free send bytes tobe

	IDPB OPT,OPP		; Copy length
	ILDB OPT,RP		; and rest
	SOSLE OPL
	  JRST .-3
	ILDB OPL,OPP		; Both point at start of next

	AOS (P)			; Skip MERGED since copied it
	RET

; IP and TCP Information Tables

ICPY==CPYOPT	; IP copy-on-fragmentation
TCPY==0		; TCP doesn't have such a bit

DEFINE INFOW (NAM,INST)<
NAM==.-..X
	INST
> ; End of INFOW


DEFINE INFO (W)<
..X=.
INFOW(CPY,<W'CPY>)
INFOW(LOF,<LOAD OPTS,T'W'OPF,(TCB)>)
INFOW(LOR,<LOAD RC,T'W'POR,(TCB)>)
INFOW(LOU,<LOAD OPP,T'W'POU,(TCB)>)
INFOW(MXW,<MAX'W'OW>)
INFOW(OTAB,<MSEC1,,W'OPNU>)
INFOW(POR,<POINT 8,TCB'W'R(TCB),7>)
INFOW(SDO,<STOR OPP,T'W'PDO,(TCB)>)
INFOW(SOF,<STOR OPTS,T'W'OPF,(TCB)>)
INFOW(SOR,<STOR RC,T'W'POR,(TCB)>)
INFOW(SOU,<STOR T3,T'W'POU,(TCB)>)
INFOW(XFOR,<XMOVEI T2,TCB'W'R(TCB)>)
INFOW(XFOU,<XMOVEI T2,TCB'W'U(TCB)>)
INFOW(XTOO,<XMOVEI T3,TCB'W'O(TCB)>)
INFOW(XTOR,<XMOVEI T3,TCB'W'R(TCB)>)
INFOW(XTOU,<XMOVEI T3,TCB'W'U(TCB)>)
	PURGE ..X
> ; End of DEFINE INFO


IPINFO:	INFO (I)		; IP Info Table


TCPINFO:INFO (T)		; TCP Info Table



; Restore registers and clean up stack

OPRES:	MOVEI 11,REGS		; Saved registers
	HRLZS 11		; REGS,,0
	BLT 11,11		; Restore them
;	MOVE 11,11+REGS		; and last
	JFCL
	RESTORE
	RET			; To R: then to caller

; SNDSOP	Send a Secure Open Option (Internet)

;TCB/	(Extended) Locked connection block
;PKT/	(Extended) Packet
;
;	CALL SNDSOP
;Ret+1:	always.

REPEAT 0,<
SNDSOP::
	TEMP <PTR>
	MOVEI T1,SOPLEN		; Length of option
	CALL INOPTR		; Get byte pointer to it
	JUMPE PTR,SNDSOX	; No room for it
	MOVEI T2,SOPOPT		; Option kind
	IDPB T2,PTR		; Store in packet
	MOVEI T2,SOPLEN		; Length
	IDPB T2,PTR
	LOAD T2,TSLVN,(TCB)	; Get next security level
	ROT T2,-8		; Do 2 bytes
	IDPB T2,PTR		; Store the high byte
	ROT T2,8
	IDPB T2,PTR		; and the low byte
SNDSOX:	RESTORE
	RET
> ; End of REPEAT 0


; SNDSCL	Send a Secure Close Option (Internet)

;PKT/	(Extended) Packet
;
;	CALL SNDSCL
;Ret+1:	always

SNDSCL::
REPEAT 0,<
	TEMP <PTR>
	MOVEI T1,SCLLEN		; Length of option
	CALL INOPTR		; Get a pointer to it
	JUMPE PTR,SNDSCX	; No space
	MOVEI T2,SCLOPT		; Kind
	IDPB T2,PTR
	MOVEI T2,SCLLEN		; Length
	IDPB T2,PTR
SNDSCX:	RESTORE
> ; End of REPEAT 0
	RET

REPEAT 0,<
; SETLAB	Set debugging label (TCP)

;T1/	Number
;PKT/	(Extended) Packet
;TPKT/	(Extended) TCP packet pointer
;
;	CALL SETLAB
;Ret+1:	always

SETLAB::TEMP <PTR,LBL>
	PUSH P,T1		; Save the number for awhile
	MOVEI T1,LBLLEN		; Length of option
	CALL TCOPTR		; Get byte pointer to it
	POP P,LBL		; Get back the argument
	JUMPE PTR,SETLBX	; No space for it
	MOVEI T3,LBLOPT
	IDPB T3,PTR
	MOVEI T3,LBLLEN		; Length
	IDPB T3,PTR
	ROT LBL,-8		; Get high byte
	IDPB LBL,PTR
	ROT LBL,8
	IDPB LBL,PTR
SETLBX:	RESTORE
	RET


; GETLAB	Get debugging label from packet (TCP)

;PKT/	(Extended) Packet
;TPKT/	(Extended) TCP packet pointer
;
;	CALL GETLAB
;Ret+1:	always, T1 having -1 or the label contents

GETLAB::TEMP <VAL,PTR,LB>
	MOVEI T1,LBLOPT		; What to look for in packet
	MOVEI T2,LBLLEN		; Expected length
	CALL FNTCOP		; Find the option
	JUMPE T1,GETLBM		; Couldn't find it.  Return -1
	MOVEM T1,PTR
	ILDB VAL,PTR		; Get high byte
	LSH VAL,8		; Position it
	ILDB LB,PTR		; Get low byte
	TROA VAL,0(LB)		; Merge the two
GETLBM:	SETO VAL,
	RESTORE
	RET
> ; End of REPEAT 0
REPEAT 0,<
; RSVTSO(PKT)	Reserver space in packet for later use to hold timestamp

;PKT/	(Extended) Packet
;TPKT/	(Extended) TCP packet pointer
;	CALL RSVTSO
;Ret+1:	Always.  No failure indication.

RSVTSO::TEMP <PTR>
	MOVEI T1,LTSLEN		; 6 byte option length
	CALL TCOPTR		; Get the space reserved
	JUMPE PTR,RSVTSX	; None available.
	MOVEI T2,LTSOPT		; Local timestamp option
	IDPB T2,PTR		; The length
	MOVEI T2,LTSLEN
	IDPB T2,PTR		; The kind
RSVTSX:	RESTORE
	RET

; SETTSO(PKT, WHEN)	Set the value of the timestamp option (TCP)

;T1/	When (milliseconds)
;PKT/	(Extended) Packet
;TPKT/	(Extended) TCP packet pointer
;	CALL SETTSO
;Ret+1:	always.

SETTSO::TEMP <PTR,WHEN>
	PUSH P,T1
	MOVEI T1,LTSOPT		; Local timestamp option
	MOVEI T2,LTSLEN		; Expected length thereof
	CALL FNTCOP		; Find that (previously reserved) option
	POP P,WHEN
	SKIPE PTR		; Must have been no space for it.
	 CALL SET4		; Set a 4-byte number into packet
	RESTORE
	RET
> ; End of REPEAT 0

; GETTSO(PKT)	Get the value stored in the timestamp option (TCP)

;PKT/	(Extended) Packet
;TPKT/	(Extended) TCP packet pointer
;	CALL GETTSO
;Ret+1:	Qlways.  T1 .LT. 0 if no timestamp, or has the 32-bit timestamp

GETTSO::REPEAT 0,<
	MOVEI T1,LTSOPT		; The option to look for
	MOVEI T2,LTSLEN		; The length we expect for it
	CALL FNTCOP		; Find the option
	SKIPN T1		; Skip if successful
	 SOSA T1		; Else return -1
	 CALL GET4 > ; End Repeat 0; Read a 4-byte word
	RET

REPEAT 0,<
; Receive Buffer Size option (TCP)

;T1/	Our buffer size (lt 2**16)
;PKT/	(Extended) pointer to packet
;TPKT/	(Extended) pointer to TCP portion of packet
;
;	CALL SETRBS
;Ret+1:	Always.

SETRBS::TEMP <PTR,SIZ>
	PUSH P,T1		; Save the size for awhile
	MOVEI T1,RBSLEN		; Length of the option
	CALL TCOPTR		; Get the space
	POP P,SIZ		; Unsave the arg
	JUMPE PTR,SETRBX	; See if we got the space
	MOVEI T3,RBSOPT		; The option number
	IDPB T3,PTR		; Store in packet
	MOVEI T3,RBSLEN		; Option length
	IDPB T3,PTR		; Insert in packet
	ROT SIZ,-8		; High byte of arg
	IDPB SIZ,PTR		; Insert it
	ROT SIZ,8		; Low byte
	IDPB SIZ,PTR		; Store it
SETRBX:	RESTORE
	RET



;PKT/	(Extended) pointer to packet
;TPKT/	(Extended) pointer to TCP portion of packet

;
;	CALL GETRBS
;Ret+1:	Always.  T1 has the buffer size or -1 if option not present

GETRBS::TEMP <SIZ,PTR,LB>
	MOVEI T1,RBSOPT		; What to look for
	MOVEI T2,RBSLEN		; How long it should be
	CALL FNTCOP		; Look in TCP option list
	JUMPE T1,GETRBM		; Jump if not there
	MOVEM T1,PTR		; Stash the pointer
	ILDB SIZ,PTR		; Get the high byte
	LSH SIZ,8		; To right place
	ILDB LB,PTR		; Get the low byte
	TROA SIZ,0(LB)		; Combine the two
GETRBM:	SETO SIZ,		; Say not present
	RESTORE
	RET
> ; End of REPEAT 0

REPEAT 0,<
; SET4(PTR, DATA, PKT)		Set a 4-byte option into a packet

;T1/	Byte pointer into packet (Indexes by PKT or TPKT)
;T2/	32-bit right justified number to be entered
;PKT/	(Extended) Packet pointer
;TPKT/	(Extended) TCP packet pointer
;
;	CALL SET4
;Ret+1:	Always

SET4:	TEMP <PTR,DATA,CNT>
	LSH DATA,4		; Left justify in word
	MOVEI CNT,4		; How many bytes
SET41:	ROT DATA,8		; Put next byte in low 8 bits
	IDPB DATA,PTR		; Store into packet
	SOJG CNT,SET41		; Loop over all bytes
	RESTORE
	RET



; GET4(PTR, PKT)		Read out a 4-byte option value

;T1/	Byte pointer into packet (Indexes by PKT or TPKT)
;PKT/	(Extended) Packet pointer
;TPKT/	(Extended) TCP packet pointer
;
;	CALL GET4
;Ret+1:	Always.  Number in T1.

GET4:	TEMP <VAL,PTR,BYT,CNT>
	MOVEI CNT,4		; How many bytes to do
	MOVEI VAL,0
GET41:	ILDB BYT,PTR
	LSH VAL,8
	IOR VAL,BYT
	SOJG CNT,GET41		; Loop over all bytes
	RESTORE
	RET

;INOPTR	Get pointer for adding a Internet option

;T1/	Length of the option
;PKT/	(Extended) pointer to the packet
;TPKT/	(Extended) pointer to the TCP portion of the packet
;
;	CALL INOPTR
;Ret+1:	Always.  T1 has pointer or 0 if no space.

INOPTR:	LOAD T2,PIDO,(PKT)	; Get current data offset
	ASH T2,2		; As a number of bytes
	CAIG T2,MINIHS		; Any options yet?
	 JRST INOPT1		; No.
	PUSH P,T1		; Save the length
	MOVEI T2,0		; Say to look for end of options
	EXCH T1,T2		; Put args in right place
	CALL FNINOP		; Find the end in the Internet list
	POP P,T3		; Get back the desired length
	SKIPE T1		; Found the end?
	CAMGE T2,T3		; Yes.  Nuff space left?
	 TCPBUG(CHK,<INOPTR: Insufficient Internet option space>,TCPOP1)
	LOAD T2,PIDO,(PKT)	; # words IN hdr and options
	SUBI T2,<MINIHS+3>/4	; Compute number of words of options
	ASH T2,2		; Number of bytes in the options area
	LDB T4,[POINT 3,T1,2]	; # empty bytes in last word
	SUB T2,T4		; Current # bytes in options area
	ADDI T2,3(T3)		; Include this new option
	ASH T2,-2		; Round up, wordwise
	STOR T2,PIDO,(PKT)	; Set as new data offset
	EXIT INOPTX

INOPT1:	ADDI T1,3		; Set to round up
	ASH T1,-2		; Convert to words
	LOAD T2,PIDO,(PKT)	; Get data offset
	ADD T1,T2
	STOR T1,PIDO,(PKT)	; Make data offset include this option
	HRLI T1,(<POINT 8,.-.(PKT)>)	; Construct pointer
INOPTX:	RET

; TCOPTR	Get pointer for adding a TCP option

;T1/	Length of the the option
;PKT/	(Extended) pointer to the packet
;TPKT/	(Extended) pointer to the TCP portion of the packet
;
;	CALL TCOPTR
;Ret+1:	Always.  T1 has pointer or 0 if no space.

TCOPTR:	LOAD T2,PTDO,(TPKT)	; Get current TCP data offset
	ASH T2,2		; In terms of bytes
	CAIG T2,MINTHS		; Any options present?
	 JRST TCOPT1		; No.
	PUSH P,T1		; Save the length
	MOVEI T2,0		; Say to look for end of options
	EXCH T1,T2		; Put args in right place
	CALL FNTCOP		; Find the end in the TCP list
	POP P,T3		; Get back the desired length
	SKIPE T1		; Found the end?
	CAMGE T2,T3		; Yes.  Nuff space available?
	 TCPBUG(CHK,<TCOPTR: Insufficient TCP option space>,TCPOP2)
	LOAD T2,PTDO,(TPKT)	; # words TCP hdr and options
	SUBI T2,<MINTHS+3>/4	; Compute number of words of options
	ASH T2,2		; Number of bytes in the options area
	LDB T4,[POINT 3,T1,2]	; # empty bytes in last word
	SUB T2,T4		; Current # bytes in options area
	ADDI T2,3(T3)		; Include this new option
	ASH T2,-2		; Round up, wordwise
	STOR T2,PTDO,(TPKT)	; Set as new TCP data offset
	EXIT TCOPTX

TCOPT1:	ADDI T1,3		; Set to round up
	ASH T1,-2		; Number of words needed
	LOAD T2,PTDO,(TPKT)	; Data offset in words
	ADD T1,T2		; Make it include this option
	STOR T1,PTDO,(TPKT)	; Set into pkt
	HRLI T1,(<POINT 8,.-.(TPKT)>)	; Construct pointer
TCOPTX:	RET

; FNINOP	Return byte pointer for reading an Internet option

;T1/	Kind to look for
;T2/	Expected length of the option or 0 for don't care
;PKT/	(Extended) Packet
;TPKT/	(Extended) TCP packet pointer
;
;	CALL FNINOP
;Ret+1:	Always.  T1 has pointer to option if present, or 0 if not.
;		T2 has option length or space left if looking for end.

FNINOP:	MOVE T3,[POINT 8,<<MINIHS+3>/4>-1(PKT),31]; Pointer to options
	LOAD T4,PIDO,(PKT)	; Current Internet data offset
	ASH T4,2		; As a byte count
	SUBI T4,MINIHS		; Max size of option space
	CALLRET SCANOP		; Scan for that option.  Ret PTR in T1.




; FNTCOP	Return byte pointer for reading an TCP option

;T1/	Kind to look for
;T2/	Expected length of the option or 0 for don't care
;PKT/	(Extended) Packet
;TPKT/	(Extended) TCP packet pointer
;
;	CALL FNTCOP
;Ret+1:	Always.  T1 has pointer to option if present, or 0 if not.
;		T2 has option length or space left if looking for end.

FNTCOP:	MOVE T3,[POINT 8,<<MINTHS+3>/4>-1(TPKT),31]; Pointer to options
	LOAD T4,PTDO,(TPKT)	; Current TCP data offset
	ASH T4,2		; As a byte count
	SUBI T4,MINTHS		; Max size of option space
	CALLRET SCANOP		; Scan for that option.  Ret PTR in T1.

; SCANOP	Return byte pointer for reading an option

;T1/	Kind to look for
;T2/	Expected length of the option
;T3/	(Extended) pointer where to start
;T4/	Maximum number of bytes to scan
;PKT/	(Extended) Packet
;TPKT/	(Extended) TCP packet pointer
;
;	CALL SCANOP
;Ret+1:	Always.  T1 has pointer to option data, or 0 if not found.
;		 T2 has length of option or space left if scanning
;			for end option.  In this case T1 is set to
;			overwrite the end option.


SCANOP:	LOCAL <KIND,EXPLEN,OPTLEN>
	DMOVEM T1,KIND		; T1 to KIND, T2 to EXPLEN

SCANO1:	JUMPLE T4,SCANO6	; Fail if entire area scanned
	MOVE T1,T3		; T1 has ptr to start of this option
	ILDB OPTLEN,T3		; Get the kind byte
	JUMPN OPTLEN,SCANO2	; Jump if not end mark
	JUMPN KIND,SCANO6	; Fail if not looking for end
	MOVE T2,T4		; Set to return space left in T2
	JRST SCANO7		; T1 has pointer to end option

SCANO2:	CAIE OPTLEN,NOPOPT	; NOP option?
	 JRST SCANO3		; No.
	CAIE KIND,NOPOPT	; Looking for padding?
	 SOJA T4,SCANO1		; No.  Reduce space left and continue.
	JRST SCANO7		; Return pointer in T1

SCANO3:	CAME OPTLEN,KIND	; Found what we are looking for?
	 JRST SCANO4		; No.  Check next one.
	ILDB OPTLEN,T3		; Get the length for real
	JUMPE EXPLEN,SCANO5	; Don't care what length is?
	CAMN OPTLEN,EXPLEN	; Or length matches?
	 JRST SCANO5		; Yes.  Go check kind.

SCANO4:	SUB T4,OPTLEN		; Advance to next option
IFNKA <	ADJBP OPTLEN,T1		; Bump pointer
	MOVE T3,OPTLEN		; And put it in standard place
>
IFKA <	IBP T1
	SOJG OPTLEN,.-1
	MOVE T3,T1
>
	JRST SCANO1		; Try next option.

SCANO5:	MOVE T1,T3		; Yes.  Get pointer to data part.
	SKIPA T2,OPTLEN		; Set to return length.
SCANO6:	SETZ T1,		; Failure indication
SCANO7:	RESTORE
	RET
> ; End of REPEAT 0

	TNXEN